package com.cs2.volunteerpostbackend.web.interceptor;

import com.alibaba.fastjson.JSON;
import com.cs2.volunteerpostbackend.po.User;
import com.cs2.volunteerpostbackend.web.dto.RespEntity;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.validation.ObjectError;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.concurrent.TimeUnit;

@Component
public class JwtInterceptor implements HandlerInterceptor {

    // redis template 在创建时默认创建<Object, Object>泛型，使用其他泛型会被认为不存在。
    // 在不指定泛型的时候，spring默认找的是默认的泛型，而当指定<String, Object>的时候会被判定不存在
    // 就是因为 <String, Object> != <Object, Object>
    @Autowired(required = false) // 设置为非必需，避免Redis未启动时启动失败
    private RedisTemplate<Object, Object> redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 对试探请求放行不做处理
        // options 请求是浏览器自动发出咨询服务器是否允许跨域的操作，不会带 jwt
        if (request.getMethod().equalsIgnoreCase("OPTIONS")) {
            return true;
        }

        String jwtToken = request.getHeader("Authorization");
        if (jwtToken == null || jwtToken.isEmpty()) {
            System.out.println("-----------------token为空-----------------------------");
            String json = JSON.toJSONString(new RespEntity("4001", "请先登录", null));
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(json);
            return false;   // 不要到达 controller
        }
        try {
//            jwtToken = jwtToken.substring(7);   // Bearer 取消掉

            // 只有当redisTemplate不为null且Redis服务可用时才尝试从Redis获取数据
            if (redisTemplate != null) {
                try {
                    // 尝试执行一个简单的Redis操作来检查Redis是否可用
                    redisTemplate.hasKey("test_connection");
                    System.out.println("/// Redis服务可用 ///");

                    if (redisTemplate.hasKey(jwtToken)) {
                        System.out.println("/// fetch user's info from redis ///");
                        User cachedUser = (User) redisTemplate.opsForValue().get(jwtToken);

                        System.out.println("cached user:" + cachedUser.toString());
                        request.setAttribute("curUser", cachedUser);
                        return true;
                    }
                } catch (Exception redisException) {
                    // Redis不可用时忽略异常，继续使用JWT解析
                    System.out.println("/// Redis服务不可用，将直接解析JWT ///");
                }
            } else {
                System.out.println("/// RedisTemplate未注入，将直接解析JWT ///");
            }

            // 如果Redis中没有找到用户信息或Redis不可用，则直接解析JWT
            System.out.println("/// fetch user's info by parsing the jwt token ///");
            Claims claims = Jwts.parser().setSigningKey("123456").parseClaimsJws(jwtToken).getBody();
            User u =new User();
            u.setId (claims.get("id").toString());
            u.setUsername((String) claims.get("username"));
            u.setAccount((String) claims.get("account"));
            u.setRole((int) claims.get("role"));
            u.setCreateTime((long) claims.get("create_time"));
            // session 在跨域的情况下，会失效
            request.setAttribute("curUser",u);  // 请求作用域(快递柜,放东西在里头，其他方法就能取)

            // 只有当redisTemplate可用时才尝试存储到Redis
            if (redisTemplate != null) {
                try {
                    // 本次取用后要将token放到redis里
                    redisTemplate.opsForValue().set(jwtToken, u, 1, TimeUnit.DAYS);
                } catch (Exception redisException) {
                    // Redis不可用时忽略存储操作
                    System.out.println("/// Redis存储失败，但不影响JWT验证 ///");
                }
            }

            // 放行到 controller 层
            return true;
        } catch (Exception e) {
            String json = JSON.toJSONString(new RespEntity("4001", "凭证过期或被篡改", null));
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(json);
            return false;
        }
    }
}